package com.sinodata.bsm.cicp.util.telnet.pool;

import java.util.HashMap;
import java.util.LinkedList;
import java.util.Map;

import org.apache.log4j.Logger;

import com.sinodata.bsm.cicp.util.telnet.JtaSSH;
import com.sinodata.bsm.cicp.util.telnet.Telnet;
import com.sinodata.bsm.cicp.util.telnet.TelnetException;

/**
 * 
 * <p>
 * Description: 创建SSH的连接池
 * </p>
 *
 * @author liulibo
 * @version 1.0

 * <p>
 * History: 
 *
 * Date                     Author         Version     Description
 * ---------------------------------------------------------------------------------
 * 2012-5-15 PM 3:32:59     liulibo        1.0         To create
 * </p>
 *
 * @since 
 * @see
 */
@Deprecated
public class SSHPool {
    private static Logger logger = Logger.getLogger(SSHPool.class);

    private final Map<String, SSHLock> sshMaps = new HashMap<String, SSHLock>();

    private static SSHPool instance = null;

    private static Map<String, Object> sshConnectLocks = new HashMap<String, Object>();

    private Object _lock = new Object();

    private SSHPool() {
        //        Collections.synchronizedMap(sshMaps);
    }

    public synchronized static SSHPool getInstance() {
        if (instance == null) {
            instance = new SSHPool();
        }
        return instance;
    }

    /**
     * 从Map中取数据时加上同步  因为它不是线程安全的
     *
     * @param host
     * @return
     */
    private synchronized SSHLock getSSHLock(String host) {
        SSHLock sshLock = sshMaps.get(host);
        if (sshLock == null) {
            sshLock = new SSHLock(host);
            sshMaps.put(host, sshLock);
        }
        return sshLock;
    }

    /**
     * 从SSH连接池中获取一个SSH一个连接
     *
     * @param host
     * @param port
     * @param user
     * @param pwd
     * @param maxSSHSize
     * @param prompt
     * @return
     */
    @Deprecated
    public Telnet getTelnet(String host, int port, String user, String pwd, int maxSSHSize, String prompt) throws Exception {
        SSHLock sshLock = getSSHLock(host);
        if (sshLock.getMaxSize() != maxSSHSize) {
            sshLock.setMaxSize(maxSSHSize);
        }
        if (!sshLock.isConnectable()) {
            logger.info("There is no availabe connections of SSH");
        }
        sshLock.lock();
        try {
            synchronized (sshLock.getSSHList()) {
                if (sshLock.getSSHList().size() > 0) {
                    return sshLock.getSSHList().removeFirst();
                }
            }
            Object lock = null;
            //新建连接时由lock锁定  lock由host唯一确定
            synchronized (sshConnectLocks) {
                lock = sshConnectLocks.get(host);
                if (lock == null) {
                    sshConnectLocks.put(host, new Object());
                    lock = sshConnectLocks.get(host);
                }
            }
            synchronized (lock) {
                JtaSSH jtaSSH = new JtaSSH();
                jtaSSH.connect(host, port);
                jtaSSH.runLogin(user, pwd, prompt);
                jtaSSH.runTask("LANG=C;export LANG"); //改为英语环境 否则解析shell时会混乱
                sshLock.setConnectable(true);
                return jtaSSH;
            }
        } catch (TelnetException te) {
            sshLock.setConnectable(false);
            logger.error("Failed when login in the ssh way!", te);
            te.printStackTrace();
            throw te;
        } catch (Exception e) {
            sshLock.setConnectable(false);
            logger.error("Failed when fetch a ssh connection!", e);
            throw new Exception(e);
        }
    }

    /**
     * 释放一个ssh连接  将其放在LinkedList中
     *
     * @param host
     * @param telnet
     */
    public void freeTelnet(String host, Telnet telnet) throws Exception {
        getSSHLock(host).free(telnet);
    }

    /**
     * 释放一个主机的全部ssh链接，供外部使用,例如重置配置信息
     * 
     * @param host
     * @throws Exception
     */
    public void freeTelnet(String host) throws Exception {
        SSHLock sshLock = getSSHLock(host);
        synchronized (_lock) {
            LinkedList<Telnet> list = sshLock.getSSHList();
            for (Telnet telnet : list) {
                telnet.setIsValid(false);
            }
        }
    }

    /**
     * 记录IP主机的ssh当前连接数和最大连接数
     *
     */
    class SSHLock {
        protected String host;
        protected int currentSize = 0;
        protected int maxSize = 10;
        protected boolean connectable = true;
        protected LinkedList<Telnet> sshList = new LinkedList<Telnet>();

        public SSHLock(String host) {
            this.host = host;
        }

        /**
         * 新增一个连接时增加当前ssh连接数 超过最大值则等待
         *
         */
        public synchronized void lock() {
            while (currentSize >= maxSize) {
                try {
                    wait();
                } catch (Exception e) {
                    logger.error("Failed to add connection!");
                }
            }
            currentSize++;
        }

        /**
         * 释放一个连接
         *
         */
        public synchronized void free(Telnet telnet) throws Exception {
            currentSize--;
            if (telnet.getIsValid() == true) {
                sshList.addLast(telnet);
            } else {
                telnet.disconnect();
            }
            notifyAll();
        }

        public synchronized boolean isConnectable() {
            return connectable || (currentSize < maxSize);
        }

        public synchronized void setConnectable(boolean connectable) {
            this.connectable = connectable;
        }

        public synchronized int getMaxSize() {
            return maxSize;
        }

        public synchronized void setMaxSize(int maxSize) {
            this.maxSize = maxSize;
        }

        public synchronized LinkedList<Telnet> getSSHList() {
            return sshList;
        }

        public synchronized void setSSHList(LinkedList<Telnet> sshList) {
            this.sshList = sshList;
        }

    }
}
